using System;
using System.Collections.Generic;
using System.Text;
using Util;

namespace Crm.Domain
{
    /// <summary>
    /// A geographic address represents a geographical location at which a party can be contacted.
    /// </summary>
    /// <remarks>This is a value object (Evans).</remarks>
	public class Address
	{
		#region Fields

        private IList<string> _addressLines;
        private string _city;
        private string _regionOrState;
        private string _zipOrPostCode;

		#endregion

		#region Constructor

        // TODO: Improve the way this is commented, its tooooo much.
        /// <summary>
        /// Creates an instance of the class.
        /// </summary>
        /// <param name="addressLines">One or more address lines. The first line is often a building name or a building number and
        /// street or a postal box number, the other address lines are made up with other location information.</param>
        /// <param name="city">The city that the address is in.</param>
        /// <param name="regionOrState">The region or state that the address is in.</param>
        /// <param name="zipOrPostCode">The zip or postal code (UK) for the address.</param>
        /// <exception cref="ArgumentNullException"><paramref name="addressLines"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentException"><paramref name="addressLines"/> is empty.</exception>
        /// <exception cref="ArgumentException"><paramref name="addressLines"/> contains any empty address lines.</exception>
        /// <exception cref="ArgumentNullException"><paramref name="city"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentException"><paramref name="city"/> is empty.</exception>
        /// <exception cref="ArgumentNullException"><paramref name="regionOrState"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentException"><paramref name="regionOrState"/> is empty.</exception>
        /// <exception cref="ArgumentNullException"><paramref name="zipOrPostCode"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentException"><paramref name="zipOrPostCode"/> is empty.</exception>
        public Address(IList<string> addressLines, string city, string regionOrState, string zipOrPostCode)
        {
            # region Preconditions

            ArgumentValidation.ThrowIfNullOrEmpty(city, "city");
            ArgumentValidation.ThrowIfNullOrEmpty(regionOrState, "regionOrState");
            ArgumentValidation.ThrowIfNullOrEmpty(zipOrPostCode, "zipOrPostCode");
            ArgumentValidation.ThrowIfNullOrEmpty(addressLines, "addressLines");

            ThrowIfContainsAnyEmptyStrings(addressLines);

            # endregion

            // defensive copy
            _addressLines = new List<string>(addressLines);

            _city = city;
            _regionOrState = regionOrState;
            _zipOrPostCode = zipOrPostCode;
        }

        /// <summary>
        /// Throws a <see cref="ArgumentException"/> if <paramref name="addressLines"/> contains any empty address strings or
        /// null values.
        /// </summary>
        /// <param name="addressLines"></param>
        private void ThrowIfContainsAnyEmptyStrings(IList<string> addressLines)
        {
            foreach (string addressLine in addressLines)
            {
                if (String.IsNullOrEmpty(addressLine))
                {
                    throw new ArgumentException("Please ensure that none of the address lines are null or empty.");
                }
            }
        }

		#endregion

		#region Public Properties

        /// <summary>
        /// Gets the address lines associated with the address. The first line is often a building name or a 
        /// building number and street or a postal box number, the other address lines are made up with other location
        /// information.
        /// </summary>
        public IEnumerable<string> AddressLines
        {
            get
            {
                return _addressLines;
            }
        }
        public string City
        {
            get
            {
                return _city;
            }
        }

        public string RegionOrState
        {
            get
            {
                return _regionOrState;
            }
        }

        public string ZipOrPostCode
        {
            get
            {
                return _zipOrPostCode;
            }
        }

        # endregion

        # region Overrides 

        /// <summary>
        /// Returns a string representation of this address.
        /// </summary>
        public override string ToString()
        {
            StringBuilder addressString = new StringBuilder();

            foreach(string addressLine in this.AddressLines)
            {
                addressString.AppendFormat("{0},{1}", addressLine, Environment.NewLine);
            }

            addressString.AppendFormat("{1},{0}{2},{0}{3}.", Environment.NewLine, _city, _regionOrState, _zipOrPostCode);

            return addressString.ToString();
        }

        # endregion
    }
}
